﻿/* ----------------------------------------------------------------------------
 * Copyright (c) Rob Ashton 2009
 *
 * This source code is subject to terms and conditions of the Microsoft Public 
 * License (Ms-PL).
 * 
 * By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 * 
 * To request a copy of the Microsoft Public License, send a request by email
 * to license@mvcex.com
 *
 * This notice must not be removed from this software.
 * ---------------------------------------------------------------------------- */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using System.Web.Mvc;
using System.Xml.Serialization;
using System.IO;
using MvcEx.Controllers;
using MvcEx.Configuration;
using System.Reflection;
using Moq;
using System.Web;
using System.Web.Routing;

namespace MvcEx.Tests
{
    [TestFixture]
    public class RegressionTests
    {
        
        // This tests for a bug where asking for a controller with a string which didn't match the case of the attribute
        // On the case container would result in no controller being returned
        // The expected result would be that the controller would come back regardless of the case of the string passed in
        [Test]
        public void ensure_controller_case_ignored()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(CaseContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the CaseDependentController controller
            Type interfaceController = factory.GetControllerType("CaseDependentController");

            // Validate we got something back
            Assert.NotNull(interfaceController);
        }

        /// <summary>
        /// Module configuration failed if the assemblies block was missing (a perfectly valid setup)
        /// </summary>
        [Test]
        public void ensure_module_without_assemblies_can_be_loaded()
        {
            XmlSerializer serializer = new XmlSerializer(typeof(MvcExModule));
            MvcExModule module = null;
            using (var stream = new FileStream("data/RegressionTests/TestModule.xml", FileMode.Open, FileAccess.Read))
            {
                module = (MvcExModule)serializer.Deserialize(stream);
            }

            Assert.NotNull(module);
            Assert.AreEqual("TestModule", module.Name);
            Assert.NotNull(module.Assemblies);
        }

        /// <summary>
        /// Fixed a bug where by this particular attribute didn't work
        /// </summary>
        [Test]
        public void accept_verb_can_be_used_on_action_container_method()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(CaseContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the CaseDependentController controller
            Type interfaceController = factory.GetControllerType("AcceptVerbActionContainer");

            // Validate we got something back
            Assert.NotNull(interfaceController);
        }

        /// <summary>
        /// Fixed a bug where by this particular attribute didn't work
        /// </summary>
        [Test]
        public void validate_input_can_be_used_on_action_container_method()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(CaseContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the CaseDependentController controller
            Type interfaceController = factory.GetControllerType("ValidateInputActionContainer");

            // Validate we got something back
            Assert.NotNull(interfaceController);
        }

        /// <summary>
        /// Fixes a bug where when testing an action container failed because it hadn't been initialised properly
        /// </summary>
        [Test]
        public void can_write_tests_on_action_containers()
        {
            var container = new TestableActionContainer();
            var result = container.Result() as ActionResult;
        }

        /// <summary>
        /// This test reproduces an error whereby an invalid cast exception is generated in Line 32 of System.Web.Mvc.ModelBinders.cs
        /// For some reason, this is incurred when a parameter doesn't have a name
        /// </summary>
        [Test]
        public void generated_types_play_ball_with_model_binders()
        {
            // Create the factory
            MvcExControllerBuilderConfiguration config = new MvcExControllerBuilderConfiguration();
            config.RegisterAssembly(typeof(CaseContainer).Assembly);
            MvcExControllerBuilder factory = new MvcExControllerBuilder(config);

            // Request the CaseDependentController controller
            Type interfaceController = factory.GetControllerType("ModelBinderActionContainer");

            // Validate we got something back
            Assert.NotNull(interfaceController);

            // Create the controller
            Object controller = Activator.CreateInstance(interfaceController);

            // Mock up some required objects
            Mock<HttpContextBase> httpContextBase = new Mock<HttpContextBase>();
            Mock<HttpRequestBase> httpRequestBase = new Mock<HttpRequestBase>();
            httpContextBase.SetupGet(x => x.Request).Returns(httpRequestBase.Object);
            
            RouteData data = new RouteData();
            data.Values["action"] = "index";
            data.Values["controller"] = "default";

            ControllerActionInvoker invoker = new ControllerActionInvoker();

            // Invoke the action
            // It should throw an InvalidOperationException (not an InvalidCastException which was the original bug)
            Assert.Throws<InvalidOperationException>(
               () => {     
                        invoker.InvokeAction(
                        new ControllerContext(
                            new System.Web.Routing.RequestContext(
                                httpContextBase.Object,
                                data
                                ), 
                             (ControllerBase)controller),
                        "Index");
                    }
            );
         }

        public class DefaultController : Controller
        {
            public ActionResult Index(TestModel model)
            {
                return View();
            }
        }

        [MvcExActionContainer("ModelBinderActionContainer")]
        public class ModelBinderActionContainer : MvcExActionContainer
        {
            public ActionResult Index(TestModel model)
            {
                return View();
            }
        }

        public class TestModel
        {
            public String Text
            {
                get;
                set;
            }
        }
        

        public class TestController : Controller
        {
            public ActionResult Result()
            {
                String data =  (String)ViewData["Blah"];
                return View();
            }
        }

        [MvcExActionContainer("AcceptVerbActionContainer")]
        public class AcceptVerbActionContainer : MvcExActionContainer
        {
            [AcceptVerbs(HttpVerbs.Get)]
            public ActionResult Result()
            {
                return View();
            }
        }


        [MvcExActionContainer("ValidateInputActionContainer")]
        public class ValidateInputActionContainer : MvcExActionContainer
        {
            [ValidateInput(false)]
            public ActionResult Result()
            {
                return View();
            }
        }

        [MvcExActionContainer("TestableActionContainer")]
        public class TestableActionContainer : MvcExActionContainer
        {
            public ActionResult Result()
            {
                return View();
            }
        }

        [MvcExActionContainer("casedependentcontroller")]
        public class CaseContainer : MvcExActionContainer
        {

        }
    }
}
